/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.i18n.manager;

import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.iec.edp.caf.commons.runtime.CafEnvironment;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import io.iec.edp.caf.i18n.I18nContext;
import io.iec.edp.caf.i18n.api.ResourceLocalizer;
import io.iec.edp.caf.i18n.utils.MyFileFilter;
import io.iec.edp.caf.i18n.utils.ResourceMetadataMapper;
import io.iec.edp.caf.msu.api.ServiceUnitAwareService;
import io.iec.edp.caf.msu.api.entity.ServiceUnitInfo;
import org.apache.commons.lang3.StringUtils;
import sun.misc.BASE64Decoder;

import javax.imageio.ImageIO;
import java.awt.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.*;
import java.util.List;

/**
 * 国际化资源服务接口实现类
 *
 * @author Vincent Man
 */
public class ResourceLocalizerImpl implements ResourceLocalizer {

    private static BASE64Decoder decoder = new BASE64Decoder();

    //文件最后修改时间集合(决定是否重新获取资源文件内容)
    private final static HashMap<String, Long> fileLastModifiedTimes = new HashMap<>();

    private final static HashMap<String, ResourceBundle> resourceSets = new HashMap<>();
    //.resmap映射文件集合
    private final static HashMap<String, HashMap<String, String>> mapperSets = new HashMap<>();
    //CAF资源的存储位置
    private String resourceRootPath = StringUtils.EMPTY;
    //su路径集合
    private final static HashMap<String, String> suPathCacheSet = new HashMap<>();

    /**
     * 基于CAF根据资源文件、资源编号返回特定语言下的文本资源信息
     *
     * @param resourceID 资源编号
     * @param contentID  元数据编号或资源文件名
     * @param currentSU  当前服务单元编号
     * @param langCode   语种编号
     * @author Vincent Lin
     */
    @Override
    public String getString(String resourceID, String contentID, String currentSU, String langCode) {
        String result = null;
        if (StringUtils.isNotEmpty(resourceID) && StringUtils.isNotEmpty(contentID)) {
            currentSU = this.setSu(currentSU);
            langCode = this.setLanguage(langCode);
            try {
                result = this.getStringByResourceID(resourceID, contentID, currentSU, langCode);
            } catch (Exception e) {
                if (e instanceof RuntimeException) {
                    e.printStackTrace();
                } else {
                    throw new RuntimeException("通过" + resourceID + "获取资源出错，请检查资源文件或者" + resourceID + "是否存在");
                }
            }
        }
        return result;
    }

    /**
     * 基于CAF根据资源文件、资源编号返回特定语言下的二进制资源信息
     *
     * @param resourceID 资源编号
     * @param contentID  元数据编号或资源文件名
     * @param currentSU  当前服务单元编号
     * @param langCode   语种编号
     * @author Vincent Lin
     */
    @Override
    public byte[] getBytes(String resourceID, String contentID, String currentSU, String langCode) {
        byte[] result = null;
        if (StringUtils.isNotEmpty(resourceID) && StringUtils.isNotEmpty(contentID)) {
            currentSU = this.setSu(currentSU);
            langCode = this.setLanguage(langCode);
            try {
                result = this.getImageByResourceID(resourceID, contentID, currentSU, langCode);
            } catch (Exception e) {
                if (e instanceof RuntimeException) {
                    e.printStackTrace();
                } else {
                    throw new RuntimeException("通过" + resourceID + "获取资源出现异常，请检查资源文件或者" + resourceID + "是否存在");
                }
            }
        }
        return result;
    }

    /**
     * 根据资源文件、资源编号返回特定语言下的图像资源信息
     *
     * @param resourceID 资源编号
     * @param contentID  元数据编号或资源文件名
     * @param currentSU  语种编号
     * @param langCode   当前服务单元
     * @return java.awt.Image 图像资源信息
     * @author Vincent Lin
     */
    @Override
    public Image getImage(String resourceID, String contentID, String currentSU, String langCode) {
        Image result = null;
        try {
            byte[] results = getBytes(resourceID, contentID, currentSU, langCode);
            if (results != null) {
                String base64String = new String(results, "UTF-8");
                byte[] bytes1 = decoder.decodeBuffer(base64String);
                ByteArrayInputStream bais = new ByteArrayInputStream(bytes1);
                result = ImageIO.read(bais);
            }
        } catch (Exception e) {
            if (e instanceof RuntimeException) {
                e.printStackTrace();
            } else {
                throw new RuntimeException("通过" + resourceID + "获取资源出现异常，请检查资源文件或者" + resourceID + "是否存在");
            }
        }
        return result;
    }

    /**
     * 元数据运行时获取资源集合
     *
     * @param metadataId 元数据ID
     * @param currentSU  当前su
     * @param langCode   语言
     * @author Vincent Lin
     */
    @Override
    public ResourceBundle getResourceSet(String metadataId, String currentSU, String langCode) {
        ResourceBundle result = null;
        if (StringUtils.isNotEmpty(metadataId)) {
            currentSU = this.setSu(currentSU);
            langCode = this.setLanguage(langCode);
            try {
                result = this.getResourceSetByMetadataID(metadataId, currentSU, langCode);
            } catch (Exception e) {
                if (e instanceof RuntimeException) {
                    e.printStackTrace();
                } else {
                    throw new RuntimeException("通过" + metadataId + "获取资源集合出现异常，请检查资源文件是否存在");
                }
            }
        }
        return result;
    }

    /**
     * 获取CAF资源默认的路径
     *
     * @return 资源根路径(server - platform - common - resources)
     */
    private String getResourceRootPath() {
        if (StringUtils.isBlank(this.resourceRootPath)) {
            this.resourceRootPath = CafEnvironment.getServerRTPath() + File.separator + "platform" + File.separator + "common" + File.separator + "resources";
        }
        return resourceRootPath;
    }

    /**
     * 获取SU的绝对路径
     *
     * @param currentSU 服务单元编号
     * @return java.lang.String
     */
    private String getSURootPath(String currentSU) {
        return this.getSuPath(currentSU) + File.separator + "resources";
    }

    /**
     * 获取su的路径
     *
     * @param su 服务单元
     * @return java.lang.String
     * @author Vincent Lin
     */
    private String getSuPath(String su) {
        String path = "";
        if (!suPathCacheSet.containsKey(su)) {
            synchronized (suPathCacheSet) {
                if (!suPathCacheSet.containsKey(su)) {
                    ServiceUnitAwareService serviceUnitAwareService = SpringBeanUtils.getBean(ServiceUnitAwareService.class);
                    if (serviceUnitAwareService != null) {
                        List<ServiceUnitInfo> sus = serviceUnitAwareService.getAllServiceUnits();
                        if (sus != null && sus.size() > 0) {
                            ServiceUnitInfo serviceInfo = sus.stream().filter(info -> su.equalsIgnoreCase(info.getName())).findFirst().orElse(null);
                            if (serviceInfo != null) {
                                suPathCacheSet.put(su, serviceInfo.getPath());
                            }
                        }
                    }
                }
            }
        }
        if (suPathCacheSet.containsKey(su) && suPathCacheSet.get(su) == null) {
            throw new RuntimeException(su + "的路径信息未找到");
        }
        return suPathCacheSet.containsKey(su) ? suPathCacheSet.get(su) : path;
    }

    /**
     * 根据资源编号返回默认语言下的资源信息
     *
     * @param resourceID 资源编号
     * @param contentID  元数据编号或资源文件名
     * @param currentSU  当前的服务单元编号
     * @param langCode   语种编号
     * @return java.lang.String 资源信息
     */
    private String getStringByResourceID(String resourceID, String contentID, String currentSU, String langCode) throws ParseException, IOException {
        String value = "";
        String fileName = this.getResourceFileName(contentID, currentSU);
        //先尝试获取自定义带语言的文件名
        ResourceBundle resourceBundle = this.getCusResourceBundle(fileName, currentSU, langCode);
        if (resourceBundle == null) {
            resourceBundle = this.extractResourceBundle(fileName, currentSU, langCode);
        }
        //如果未取出，则按原文件名获取
        if (resourceBundle != null) {
            try {
                value = resourceBundle.getString(resourceID);
            } catch (MissingResourceException e) {
                //找到资源文件，但是资源文件中没有资源的报错异常
            }
        }
        return value;
    }

    /**
     * 根据资源编号返回默认语言下的资源信息
     *
     * @param resourceID 资源编号
     * @param contentID  资源文件名称
     * @param currentSU  当前的服务单元编号
     * @param langCode   语种编号
     * @return 资源信息
     */
    private byte[] getImageByResourceID(String resourceID, String contentID, String currentSU, String langCode) throws ParseException, IOException {
        byte[] value = null;
        String fileName = this.getResourceFileName(contentID, currentSU);
        //先尝试获取自定义带语言的文件名
        ResourceBundle resourceBundle = this.getCusResourceBundle(fileName, currentSU, langCode);
        if (resourceBundle == null) {
            resourceBundle = this.extractResourceBundle(fileName, currentSU, langCode);
        }
        //如果未取出，则按原文件名获取
        if (resourceBundle != null) {
            try {
                value = resourceBundle.getString(resourceID).getBytes(StandardCharsets.UTF_8);
            } catch (MissingResourceException e) {
                //找到资源文件，但是资源文件中没有资源的报错异常
            }
        }
        return value;
    }

    /**
     * 根据元数据ID返回指定SU下特定语言的所有资源集合
     *
     * @param metadataId 元数据ID
     * @param currentSU  指定的SU
     * @param langCode   语言
     * @return java.utils.ResourceBundle key-value资源包
     */
    private ResourceBundle getResourceSetByMetadataID(String metadataId, String currentSU, String langCode) throws ParseException, IOException {
        String fileName = getFileName(metadataId, currentSU);
        return this.extractResourceBundle(fileName, currentSU, langCode);
    }

    /**
     * 得到指定SU目录下工程名与MetadataId的所有映射集
     *
     * @param currentSU 所在SU
     * @return java.utils.HashMap<java.lang.String, java.lang.String> 元数据编号与工程名映射集合
     */
    private HashMap<String, String> getMapperSet(String currentSU) throws IOException {
        String rsText = "";
        HashMap<String, String> map = new HashMap<>();
        //资源文件路径下路径信息
        String resourceRootPath = "";
        if (currentSU.equalsIgnoreCase("CAF")) {
            resourceRootPath = getResourceRootPath();
        } else
            resourceRootPath = this.getSURootPath(currentSU);

        //读取资源文件路径下所有后缀为.resmap的文件信息
        File file = new File(resourceRootPath); //获得指定路径
        MyFileFilter fileFilter = new MyFileFilter("resmap"); //文件的后缀名
        String[] files = file.list(fileFilter);
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
        if (files != null) {
            for (String fileName : files) {
                File fullFile = new File(resourceRootPath.toString() + File.separator + fileName);
                ResourceMetadataMapper rm = mapper.readValue(fullFile, ResourceMetadataMapper.class);
                if (rm.getMetadataIds() != null) {
                    for (String s : rm.getMetadataIds()) {
                        map.put(s.toString(), rm.getProjectName());
                    }
                }
            }
        }
        return map;
    }

    /**
     * 根据资源元数据ID、SU、语言获取该元数据对应的资源文件名
     *
     * @param metadataId 资源元数据ID
     * @param currentSU  SU
     * @return java.lang.String 资源文件名
     */
    private String getFileName(String metadataId, String currentSU) throws IOException {
        String projectName = "";
        if (!mapperSets.containsKey(currentSU)) {
            synchronized (mapperSets) {
                if (!mapperSets.containsKey(currentSU)) {
                    HashMap<String, String> rsMappers = getMapperSet(currentSU);
                    mapperSets.put(currentSU, rsMappers);
                    projectName = rsMappers.get(metadataId);
                } else {
                    projectName = mapperSets.get(currentSU).get(metadataId);
                }
            }
        } else {
            projectName = mapperSets.get(currentSU).get(metadataId);
        }
        if (StringUtils.isEmpty(projectName))
            return null;
        return projectName;
    }

    /**
     * 根据文件名、语种获取ResourceBundle
     *
     * @param fileName  文件名
     * @param currentSU 服务单元
     * @param langCode  语种编号
     * @return java.utils.ResourceBundle 资源包
     */
    private ResourceBundle extractResourceBundle(String fileName, String currentSU, String langCode) {
        String dimension = String.format("%s%s%s", langCode, currentSU, fileName);
        String fullName;
        if (currentSU.equalsIgnoreCase("CAF")) {
            //su为CAF(先找多语资源，文件不存在则定义为默认语言)
            fullName = this.getResourceRootPath() + File.separator + langCode + File.separator + String.format("%s.%s.properties", fileName, langCode);
            if (!new File(fullName).exists())
                fullName = this.getResourceRootPath() + File.separator + String.format("%s.properties", fileName);
        } else {
            //su为其他(逻辑同上)
            fullName = this.getSURootPath(currentSU) + File.separator + langCode + File.separator + String.format("%s.%s.properties", fileName, langCode);
            if (!new File(fullName).exists())
                fullName = this.getSURootPath(currentSU) + File.separator + String.format("%s.properties", fileName);
        }
        //默认语言资源不存在，返回null
        if (!new File(fullName).exists())
            return null;

        //resourceBundle不存在，第一次获取
        if (!resourceSets.containsKey(dimension)) {
            synchronized (resourceSets) {
                if (!resourceSets.containsKey(dimension)) {
                    fileLastModifiedTimes.put(dimension, new File(fullName).lastModified());
                    ResourceBundle value = getResourceBundle(fullName);
                    resourceSets.put(dimension, value);
                }
            }
        }
        //resourceBundle存在，但是文件已经更新
        if (resourceSets.containsKey(dimension) && !fileLastModifiedTimes.get(dimension).equals(new File(fullName).lastModified())) {
            synchronized (resourceSets) {
                if (!fileLastModifiedTimes.get(dimension).equals(new File(fullName).lastModified())) {
                    //resourceBundle不存在，第一次获取
                    fileLastModifiedTimes.put(dimension, new File(fullName).lastModified());
                    ResourceBundle value = getResourceBundle(fullName);
                    resourceSets.put(dimension, value);
                }
            }
        }
        return resourceSets.get(dimension);
    }

    /**
     * 根据文件路径从外部读取资源包
     *
     * @param filePath 文件路径
     * @return java.utils.ResourceBundle 资源包
     */
    private ResourceBundle getResourceBundle(String filePath) {
        ResourceBundle rb = null;
        BufferedInputStream inputStream;
        try {
            inputStream = new BufferedInputStream(new FileInputStream(filePath));
            InputStreamReader in = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
            rb = new PropertyResourceBundle(in);
            inputStream.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            return rb;
        }
    }

    //设置语言信息
    private String setLanguage(String language) {
        if (StringUtils.isEmpty(language)) {
            return I18nContext.current.getLanguage();
        }
        return language;
    }

    //设置su信息
    private String setSu(String su) {
        if (StringUtils.isEmpty(su)) {
            return "CAF";
        }
        return su;
    }

    //获取文件名称
    private String getResourceFileName(String contentID, String su) throws IOException {
        if (StringUtils.endsWithIgnoreCase(contentID, ".properties")) {
            //传入为文件名
            return contentID.trim().substring(0, contentID.length() - 11);
        } else {
            //传入为资源元数据ID
            return getFileName(contentID, su);
        }
    }

    /**
     * 获取自定义资源包
     *
     * @param fileName  文件名
     * @param currentSU 指定SU
     * @param langCode  语种
     * @return java.utils.ResourceBundle 资源包
     */
    private ResourceBundle getCusResourceBundle(String fileName, String currentSU, String langCode) {
        //尝试获取自定义带语言的文件名
        String cusFileName = this.getCustomResourceName(fileName);
        return this.extractResourceBundle(cusFileName, currentSU, langCode);
    }

    /**
     * 根据文件名称获取用户扩展的资源文件
     *
     * @param fileName 文件名
     * @return java.lang.String 自定义文件名
     */
    private String getCustomResourceName(String fileName) {
        return String.format("%s.custom", (Object) fileName);
    }

}

